
#include <vector>
#include "AESSymmKey.h"
#include "RSAPublicKey.h"
#include "RSAPrivateKey.h"
#include "CryptEChain.h"
#include "DecryptWrapper.h"

void DecryptWrapper::InitDecryptWrapper(const char *idkeyname, DecryptionKey *deckey, const char *ickeyname, CheckingKey *chkkey, int decompress)
{
   if (idkeyname)
   {
      dkeyname = (char *)malloc(sizeof(char)*(strlen(idkeyname)+1));
      strcpy(dkeyname, idkeyname);
   } else
   {
      dkeyname = NULL;
   }
   if (deckey)
   {
      switch (deckey->GetKeyType())
      {
         case KEY_TYPE_PRIVATE_RSA:
         {
            RSAPrivateKey *rsaprvkey =
               new RSAPrivateKey(*((RSAPrivateKey *)deckey));
            deckeycopy = (RSAPrivateKey *)rsaprvkey;
            break;
         }
         case KEY_TYPE_SYMM_AES:
         {
            AESSymmKey *aessymmkey =
               new AESSymmKey(*((AESSymmKey *)deckey));
            deckeycopy = (AESSymmKey *)aessymmkey;
            break;
         }
         default:
         {
            errprint("decrypt wrapper has no key: can't do decryption!");
            deckeycopy = NULL;
            break;
         }
      }
   } else
   {
      deckeycopy = NULL;
   }
   if (ickeyname)
   {
      ckeyname = (char *)malloc(sizeof(char)*(strlen(ickeyname)+1));
      strcpy(ckeyname, ickeyname);
   } else
   {
      ckeyname = NULL;
   }
   if (chkkey)
   {
      switch (chkkey->GetKeyType())
      {
         case KEY_TYPE_PUBLIC_RSA:
         {
            RSAPublicKey *rsapubkey =
               new RSAPublicKey(*((RSAPublicKey *)deckey));
            chkkeycopy = (RSAPublicKey *)rsapubkey;
            break;
         }
         default:
         {
            errprint("unknown signature check key type");
            chkkeycopy = NULL;
            break;
         }
      }
   } else
   {
      chkkeycopy = NULL;
   }
   dodecompress = (decompress != 0);
   wastrusted = 0;
   apr_status_t status;
   status = apr_pool_create(&mp, NULL);
   CheckAPRError(status);
}

DecryptWrapper::DecryptWrapper(const char *idkeyname, DecryptionKey *deckey)
{
   InitDecryptWrapper(idkeyname, deckey, NULL, NULL, FALSE);
}

DecryptWrapper::DecryptWrapper(const char *idkeyname, DecryptionKey *deckey, const char *ickeyname, CheckingKey *chkkey)
{
   InitDecryptWrapper(idkeyname, deckey, ickeyname, chkkey, FALSE);
}

DecryptWrapper::DecryptWrapper(const char *idkeyname, DecryptionKey *deckey, int dodecompress)
{
   InitDecryptWrapper(idkeyname, deckey, NULL, NULL, dodecompress);
}

DecryptWrapper::DecryptWrapper(const char *idkeyname, DecryptionKey *deckey, const char *ickeyname, CheckingKey *chkkey, int dodecompress)
{
   InitDecryptWrapper(idkeyname, deckey, ickeyname, chkkey, dodecompress);
}

DecryptWrapper::~DecryptWrapper()
{
   if (dkeyname) free(dkeyname);
   if (deckeycopy) delete deckeycopy;
   if (ckeyname) free(ckeyname);
   if (chkkeycopy) delete chkkeycopy;
   apr_pool_destroy(mp);
}

std::vector<char> *DecryptWrapper::DecryptBuffer(const char *buffer, size_t length)
{
   int smallblock = (ENV_READ_BUFFER > (length/2)) ? (length/2) : ENV_READ_BUFFER;
   size_t nlength = length;
   APRBucketBrigade *inputbb = new APRBucketBrigade(mp);
   if (inputbb->BufferToBucketBrigade(buffer, &length, smallblock) != 0)
   {
      errprint("failed to push the buffer into a bucket");
      delete inputbb;
      return NULL;
   }
   if (nlength != length)
   {
      errprint("bucket generation failed, pushed %d bytes to bucket, wanted %d", nlength, length);
      delete inputbb;
      return NULL;
   }
   APRBucketBrigade *outputbb = DecryptToBucket(inputbb);
   delete inputbb;
   char *outputbuffer = NULL;
   size_t ilength = 0;
   std::vector<char> *newstring = NULL;
   if (outputbb->BucketBrigadeToBuffer(&outputbuffer, &ilength) == 0)
   {
      // outputbuffer cannot be NULL!
      newstring = new std::vector<char>();
      newstring->insert(newstring->end(), outputbuffer, &(outputbuffer[ilength]));
   } else
   {
      errprint("failed to put bucket into buffer");
   }
   if (outputbuffer) free(outputbuffer);
   delete outputbb;
   return newstring;
}

APRBucketBrigade *DecryptWrapper::DecryptToBucket(APRBucketBrigade *bbrgde)
{
   CryptEChain *crypty = new CryptEChain();
   crypty->BuildDecryptionChain2(deckeycopy, chkkeycopy, dodecompress, bbrgde->RealLength());
   APRBucketBrigade *outputbrgde = new APRBucketBrigade(mp);
   int prociter = 0;
   while (prociter == 0)
      prociter = crypty->ProcessIteration(bbrgde, outputbrgde);
   delete bbrgde;
   wastrusted = crypty->IsTrusted();
   delete crypty;
   if (prociter == -1)
   {
      delete outputbrgde;
      return NULL;
   }
   return outputbrgde;
}
